import os
import sys
import tkinter as tk
from tkinter import filedialog, ttk, messagebox, scrolledtext
import datetime
import threading
import stat
from jar_processor import JarProcessor
from utils import normalize_jar_path, jar_path_to_system_path
from excel_parser import ExcelParser
from jar_processor import JarProcessor
from update_executor import JarUpdaterCore

class JarUpdaterGUI:
    def __init__(self, root):
        self.root = root
        self.root.title("JAR包自动更新工具")
        self.root.geometry("800x650")
        self.root.resizable(True, True)
        
        # 设置中文字体支持
        self.setup_fonts()
        
        # 变量初始化
        self.jar_path = tk.StringVar()
        self.zip_path = tk.StringVar()
        self.excel_path = tk.StringVar()
        self.output_path = tk.StringVar()
        self.progress_var = tk.DoubleVar()
        self.status_var = tk.StringVar(value="就绪")
        self.process_log = []  # 存储处理进度日志
        self.core = JarUpdaterCore(self)  # 业务核心实例
        self.excel_parser = ExcelParser()
        self.folder_replace_mode = tk.BooleanVar(value=False)  # 是否文件夹替换模式
        
        # 创建界面
        self.create_widgets()

    def _choose_from_list(self, title, prompt, options):
        """弹出带列表选择的对话框，返回选中索引或None"""
        win = tk.Toplevel(self.root)
        win.title(title)
        win.grab_set()
        win.geometry("640x420")
        ttk.Label(win, text=prompt, wraplength=600, justify=tk.LEFT).pack(fill=tk.X, padx=12, pady=8)
        listbox = tk.Listbox(win, selectmode=tk.SINGLE)
        listbox.pack(fill=tk.BOTH, expand=True, padx=12, pady=8)
        for i, opt in enumerate(options):
            listbox.insert(tk.END, f"[{i+1}] {opt}")
        sel = {'idx': None}
        def on_ok():
            try:
                idxs = listbox.curselection()
                if not idxs:
                    messagebox.showwarning("提示", "请先选择一项")
                    return
                sel['idx'] = idxs[0]
                win.destroy()
            except Exception:
                win.destroy()
        def on_cancel():
            sel['idx'] = None
            win.destroy()
        btn_frame = ttk.Frame(win)
        btn_frame.pack(fill=tk.X, padx=12, pady=8)
        ttk.Button(btn_frame, text="确定", command=on_ok).pack(side=tk.RIGHT, padx=6)
        ttk.Button(btn_frame, text="取消", command=on_cancel).pack(side=tk.RIGHT)
        win.wait_window()
        return sel['idx']

    # GUI 交互：当压缩包内存在多个同名文件，选择映射
    def choose_zip_mapping(self, filename, dest_path, candidates):
        opts = [f"{c['path']} | 时间戳: {c['timestamp']}" for c in candidates]
        idx = self._choose_from_list(
            title="选择替换源文件",
            prompt=f"发现多个同名文件：{filename}\n目标路径：{dest_path}\n请选择要使用的源文件：",
            options=opts
        )
        return idx

    # GUI 交互：当Excel路径与JAR现有位置不一致时，选择放置位置
    def choose_dest_path(self, filename, excel_dest, jar_candidates, pkg_guess=None):
        options = [f"Excel指定路径: {excel_dest} (优先)" ]
        options += [f"JAR现有路径: {p}" for p in jar_candidates]
        if pkg_guess and pkg_guess not in options:
            options.append(f"按类包路径推断: {pkg_guess}")
        idx = self._choose_from_list(
            title="选择目标路径",
            prompt=f"文件：{filename}\nExcel路径与JAR现有位置不一致，选择使用的目标路径：",
            options=options
        )
        return idx, options

    # GUI 交互：压缩包中存在Excel未列出的额外文件，是否处理
    def confirm_include_extras(self, extra_paths):
        msg = "发现以下压缩包文件未在Excel中列出，将默认不替换：\n\n" + "\n".join(extra_paths[:20])
        if len(extra_paths) > 20:
            msg += f"\n... 等 {len(extra_paths)-20} 个"
        msg += "\n\n是否改为全部替换这些额外文件？"
        return messagebox.askyesno("额外文件处理", msg)
    
    def setup_fonts(self):
        """设置字体以支持中文显示"""
        if sys.platform.startswith('win'):
            default_font = ('SimHei', 10)
        elif sys.platform.startswith('linux'):
            default_font = ('WenQuanYi Micro Hei', 10)
        else:
            default_font = ('Arial Unicode MS', 10)
            
        self.root.option_add("*Font", default_font)
    
    def create_widgets(self):
        """创建GUI组件"""
        # 创建主框架
        main_frame = ttk.Frame(self.root, padding="20")
        main_frame.pack(fill=tk.BOTH, expand=True)
        
        # JAR包选择
        ttk.Label(main_frame, text="原始JAR包:", foreground="red").grid(row=0, column=0, sticky=tk.W, pady=5)
        ttk.Entry(main_frame, textvariable=self.jar_path, width=50).grid(row=0, column=1, pady=5)
        ttk.Button(main_frame, text="浏览...", command=self.browse_jar).grid(row=0, column=2, padx=5, pady=5)

        # 替换文件压缩包选择
        ttk.Label(main_frame, text="替换文件压缩包:", foreground="red").grid(row=1, column=0, sticky=tk.W, pady=5)
        ttk.Entry(main_frame, textvariable=self.zip_path, width=50).grid(row=1, column=1, pady=5)
        ttk.Button(main_frame, text="浏览...", command=self.browse_zip).grid(row=1, column=2, padx=5, pady=5)
        
        # Excel说明文件选择（必填）
        ttk.Label(main_frame, text="Excel路径文件 (必填):", foreground="red").grid(row=2, column=0, sticky=tk.W, pady=5)
        ttk.Entry(main_frame, textvariable=self.excel_path, width=50).grid(row=2, column=1, pady=5)
        ttk.Button(main_frame, text="浏览...", command=self.browse_excel).grid(row=2, column=2, padx=5, pady=5)
        
        # 输出文件路径
        ttk.Label(main_frame, text="输出JAR包路径:").grid(row=3, column=0, sticky=tk.W, pady=5)
        ttk.Entry(main_frame, textvariable=self.output_path, width=50).grid(row=3, column=1, pady=5)
        ttk.Button(main_frame, text="浏览...", command=self.browse_output).grid(row=3, column=2, padx=5, pady=5)
        
        # 进度条
        ttk.Label(main_frame, text="处理进度:").grid(row=4, column=0, sticky=tk.W, pady=10)
        self.progress_bar = ttk.Progressbar(main_frame, variable=self.progress_var, length=400)
        self.progress_bar.grid(row=4, column=1, pady=10)
        
        # 状态标签
        self.status_label = ttk.Label(main_frame, textvariable=self.status_var, foreground="blue")
        self.status_label.grid(row=5, column=0, columnspan=3, sticky=tk.W, pady=5)
        
        # 处理进度区域
        ttk.Label(main_frame, text="更新进度:").grid(row=6, column=0, sticky=tk.NW, pady=5)
        self.process_text = scrolledtext.ScrolledText(main_frame, width=60, height=8, wrap=tk.WORD)
        self.process_text.grid(row=6, column=1, columnspan=2, pady=5, sticky=tk.W+tk.E+tk.N+tk.S)
        self.process_text.config(state=tk.DISABLED)  # 初始设为只读
        
        # 操作按钮
        button_frame = ttk.Frame(main_frame)
        button_frame.grid(row=7, column=0, columnspan=3, pady=10)
        
        ttk.Button(button_frame, text="开始更新", command=self.start_update, width=15).pack(side=tk.LEFT, padx=10)
        ttk.Button(button_frame, text="清除", command=self.clear_fields, width=15).pack(side=tk.LEFT, padx=10)
        ttk.Button(button_frame, text="退出", command=self.root.quit, width=15).pack(side=tk.LEFT, padx=10)
        
        ttk.Checkbutton(
            main_frame, 
            text="文件夹替换模式", 
            variable=self.folder_replace_mode
        ).grid(row=7, column=0, columnspan=3, sticky=tk.W, pady=5)

        ttk.Label(
            main_frame, 
            text="说明: 启用文件夹替换模式后，会先删除目标文件夹中的所有文件，再添加新文件", 
            font=("SimHei", 9), 
            foreground="blue"
        ).grid(row=8, column=0, columnspan=3, pady=5)

        # 说明文本
        ttk.Label(
            main_frame, 
            text="注意: Excel文件必须包含表头 'lib包'(可选), '路径'(必填), '文件名'(必填)", 
            font=("SimHei", 9), 
            foreground="red"
        ).grid(row=9, column=0, columnspan=3, pady=5)
        
        ttk.Label(
            main_frame, 
            text="说明: 带lib包的文件会被添加到BOOT-INF/lib/[lib包]/下，保持原始文件时间戳", 
            font=("SimHei", 9)
        ).grid(row=10, column=0, columnspan=3, pady=5)
        
        # 调整列宽和行高
        for i in range(3):
            main_frame.grid_columnconfigure(i, weight=1)
        main_frame.grid_rowconfigure(6, weight=1)  # 处理进度区域可扩展
    
    def browse_jar(self):
        filename = filedialog.askopenfilename(
            parent=self.root,
            title="选择JAR包",
            filetypes=[("JAR文件", "*.jar"), ("所有文件", "*.*")]
        )
        if filename:
            self.jar_path.set(filename)
            if not self.output_path.get():
                dir_name, file_name = os.path.split(filename)
                name, ext = os.path.splitext(file_name)
                
                # 生成年月日+两位序列的后缀（例如：2024052001）
                today = datetime.date.today().strftime("%Y%m%d")
                
                # 查找当前目录下相同前缀的文件，确定序列数
                sequence = 1
                while True:
                    suffix = f"{today}{sequence:02d}"
                    output_filename = f"{name}_{suffix}{ext}"
                    output_path = os.path.join(dir_name, output_filename)
                    
                    if not os.path.exists(output_path):
                        break
                    sequence += 1
                    if sequence > 99:  # 最大支持99个序列
                        raise Exception("同一天内生成的文件已超过99个，请手动指定输出路径")
                
                self.output_path.set(output_path)
    
    def browse_zip(self):
        filename = filedialog.askopenfilename(
            parent=self.root,
            title="选择替换文件压缩包",
            filetypes=[("压缩包", "*.zip"),("压缩包", "*.rar"),("所有文件", "*.*")]  # 添加RAR格式
        )
        if filename:
            self.zip_path.set(filename)
    
    def browse_excel(self):
        filename = filedialog.askopenfilename(
            parent=self.root,
            title="选择Excel路径文件",
            filetypes=[("Excel文件", "*.xlsx"), ("Excel文件", "*.xlx"),("所有文件", "*.*")]
        )
        if filename:
            self.excel_path.set(filename)
    
    def browse_output(self):
        filename = filedialog.asksaveasfilename(
            parent=self.root,
            title="保存输出JAR包",
            defaultextension=".jar",
            filetypes=[("JAR文件", "*.jar"), ("所有文件", "*.*")]
        )
        if filename:
            self.output_path.set(filename)
    
    def clear_fields(self):
        self.jar_path.set("")
        self.zip_path.set("")
        self.excel_path.set("")
        self.output_path.set("")
        self.progress_var.set(0)
        self.status_var.set("就绪")
        self.process_log = []
        self.core.clear_state()
        self._update_process_text()
    
    def _update_process_text(self):
        self.process_text.config(state=tk.NORMAL)
        self.process_text.delete(1.0, tk.END)
        if self.process_log:
            for log in self.process_log:
                self.process_text.insert(tk.END, log + "\n\n")
        else:
            self.process_text.insert(tk.END, "等待开始更新...")
        self.process_text.see(tk.END)  # 滚动到最新内容
        self.process_text.config(state=tk.DISABLED)
    
    def _log_process(self, message):
        timestamp = datetime.datetime.now().strftime("%H:%M:%S")
        self.process_log.append(f"[{timestamp}] {message}")
        self.root.after(100, self._update_process_text)
    
    def start_update(self):
        """开始更新JAR包的处理"""
        # 清除之前的处理日志
        self.process_log = []
        self._update_process_text()
        
        # 验证输入
        if not self.jar_path.get():
            error_msg = "请选择原始JAR包"
            self._log_process(error_msg)
            messagebox.showerror("错误", error_msg)
            return
        
        if not self.zip_path.get():
            error_msg = "请选择替换文件压缩包"
            self._log_process(error_msg)
            messagebox.showerror("错误", error_msg)
            return
        
        if not self.excel_path.get():
            error_msg = "请选择Excel路径文件（必填）"
            self._log_process(error_msg)
            messagebox.showerror("错误", error_msg)
            return
        
        if not self.output_path.get():
            error_msg = "请指定输出JAR包路径"
            self._log_process(error_msg)
            messagebox.showerror("错误", error_msg)
            return
        
        # 检查文件是否存在
        if not os.path.exists(self.jar_path.get()):
            error_msg = f"JAR包文件不存在: {self.jar_path.get()}"
            self._log_process(error_msg)
            messagebox.showerror("错误", error_msg)
            return
        
        if not os.path.exists(self.zip_path.get()):
            error_msg = f"替换文件压缩包不存在: {self.zip_path.get()}"
            self._log_process(error_msg)
            messagebox.showerror("错误", error_msg)
            return
        
        if not os.path.exists(self.excel_path.get()):
            error_msg = f"Excel文件不存在: {self.excel_path.get()}"
            self._log_process(error_msg)
            messagebox.showerror("错误", error_msg)
            return
        
        # 解析Excel文件获取替换信息
        replace_info_list, msg = self.excel_parser.parse_replace_info(self.excel_path.get())
       
        if not replace_info_list:
            messagebox.showerror("Excel解析错误", msg)
            return
        
        # 检查输出路径权限
        output_dir = os.path.dirname(self.output_path.get())
        if not os.path.exists(output_dir):
            try:
                os.makedirs(output_dir, exist_ok=True)
                perm_ok = True
                perm_msg = "输出目录创建成功"
            except Exception as e:
                perm_ok = False
                perm_msg = f"创建输出目录失败: {str(e)}"
        elif not os.access(output_dir, os.W_OK):
            perm_ok = False
            perm_msg = "没有输出目录写入权限"
        else:
            perm_ok = True
            perm_msg = "权限检查通过"
        if not perm_ok:
            self._log_process(perm_msg)
            messagebox.showerror("权限错误", perm_msg)
            desktop = os.path.join(os.path.expanduser("~"), "Desktop")
            if os.path.exists(desktop):
                self.output_path.set(os.path.join(desktop, os.path.basename(self.output_path.get())))
                messagebox.showinfo("路径建议", f"已自动将输出路径设置为桌面: {self.output_path.get()}")
            return
        
        # 禁用按钮防止重复操作
        for child in self.root.winfo_children():
            for btn in child.winfo_children():
                if isinstance(btn, ttk.Button) and btn["text"] == "开始更新":
                    btn.config(state=tk.DISABLED)
        
        # 在新线程中执行更新操作
        threading.Thread(
            target=self.core.perform_update, 
            args=(
                self.jar_path.get(), 
                self.zip_path.get(), 
                self.excel_path.get(), 
                self.output_path.get(),
                replace_info_list,
                self.status_var,
                self._log_process,
                self.progress_var,
                self.folder_replace_mode.get()  # 添加新参数
            ), 
            daemon=True
        ).start()